package org.eclipsecon.dui.interpreter.internal;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.emf.databinding.EMFObservables;
import org.eclipse.emf.databinding.EMFUpdateValueStrategy;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
import org.eclipsecon.dui.databinding.DBinding;
import org.eclipsecon.dui.databinding.DControlBinding;
import org.eclipsecon.dui.databinding.DModel;
import org.eclipsecon.dui.databinding.DTemplateBinding;
import org.eclipsecon.dui.databinding.PathElement;
import org.eclipsecon.dui.databinding.PatternValidator;
import org.eclipsecon.dui.databinding.util.DatabindingSwitch;
import org.eclipsecon.dui.swtmodel.MCombo;
import org.eclipsecon.dui.swtmodel.MText;
import org.eclipsecon.dui.swtmodel.TemplateUsage;
import org.eclipsecon.dui.swtmodel.util.SwtmodelSwitch;

public class BindingSwitch extends DatabindingSwitch {

	public class ValidaitonSwitch extends DatabindingSwitch<IValidator> {

		private final Control control;

		public ValidaitonSwitch(
				Control control) {
			this.control = control;
		}

		@Override
		public IValidator casePatternValidator(PatternValidator object) {
			String pattern = object.getPattern();
			pattern = pattern.replace("\\\\", "\\");
			
			
			
			return new RegexValidator(object
					.getMessage(), pattern, createDecorator(
					control, object.getMessage(), object.isError()));
		}
		
		private ControlDecoration createDecorator(Control control,
				String message, boolean isError) {
			ControlDecoration controlDecoration = new ControlDecoration(
					control, SWT.LEFT | SWT.TOP);
			controlDecoration.setDescriptionText(message);
			FieldDecoration fieldDecoration = FieldDecorationRegistry
					.getDefault().getFieldDecoration(
							isError ? FieldDecorationRegistry.DEC_ERROR
									: FieldDecorationRegistry.DEC_WARNING);
			controlDecoration.setImage(fieldDecoration.getImage());
			return controlDecoration;
		}

	}

	public class Pair {

		private final EStructuralFeature feature;
		private final EObject modelElement;

		public Pair(EObject modelElement, EStructuralFeature feature) {
			this.modelElement = modelElement;
			this.feature = feature;
		}

		public EStructuralFeature getFeature() {
			return feature;
		}

		public EObject getModelElement() {
			return modelElement;
		}

	}

	public class SWTBindingSwitch extends SwtmodelSwitch<ISWTObservableValue> {

		private TemplateUsage context = null;

		public SWTBindingSwitch(TemplateUsage template) {
			this.context = template;
		}

		public SWTBindingSwitch() {
		}

		@Override
		public ISWTObservableValue caseMText(MText object) {
			SWTControlAdapter adapter = (SWTControlAdapter) EcoreUtil
					.getAdapter(object.eAdapters(), SWTControlAdapter.class);
			return SWTObservables.observeText(adapter.getControl(context),
					SWT.Modify);
		}

		@Override
		public ISWTObservableValue caseMCombo(MCombo object) {
			SWTControlAdapter adapter = (SWTControlAdapter) EcoreUtil
					.getAdapter(object.eAdapters(), SWTControlAdapter.class);
			return SWTObservables.observeSelection(adapter.getControl(context));
		}
	}

	private final DataBindingContext bindingContext;
	private final EObject model;

	public BindingSwitch(DataBindingContext bindingContext, EObject model) {
		this.bindingContext = bindingContext;
		this.model = model;
	}

	@Override
	public Object caseDModel(DModel object) {
		for (DBinding binding : object.getBindings()) {
			doSwitch(binding);
		}
		return null;
	}

	@Override
	public Object caseDControlBinding(DControlBinding object) {
		ISWTObservableValue swtObs = new SWTBindingSwitch().doSwitch(object
				.getControl());

		Pair pair = findModelElement(model, object.getPath());

		IObservableValue emfObs = createEMFObservable(pair);
		EMFUpdateValueStrategy targetToModelStrategy = new EMFUpdateValueStrategy();
		bindingContext.bindValue(swtObs, emfObs, targetToModelStrategy,
				new EMFUpdateValueStrategy());
		applyValidator(object, swtObs, targetToModelStrategy);
		return null;
	}

	private void applyValidator(DBinding object,
			ISWTObservableValue swtObs,
			EMFUpdateValueStrategy targetToModelStrategy) {
		if (object.getValidator() != null) {
			IValidator valiadator = new ValidaitonSwitch((Control) swtObs
					.getWidget()).doSwitch(object.getValidator());
			targetToModelStrategy.setAfterConvertValidator(valiadator);
			targetToModelStrategy.validateAfterConvert(swtObs.getValue());
		}
	}

	@Override
	public Object caseDTemplateBinding(DTemplateBinding object) {
		ISWTObservableValue swtObs = new SWTBindingSwitch(object.getTemplate())
				.doSwitch(object.getControl());

		Pair pair = findModelElement(model, object.getPath());

		IObservableValue emfObs = createEMFObservable(pair);
		EMFUpdateValueStrategy targetToModelStrategy = new EMFUpdateValueStrategy();
		bindingContext.bindValue(swtObs, emfObs, targetToModelStrategy,
				new EMFUpdateValueStrategy());
		applyValidator(object, swtObs, targetToModelStrategy);
		return null;
	}

	private Pair findModelElement(EObject modelElement, PathElement path) {
		EStructuralFeature feature = path.getAttribute();
		//modelElement.eClass().getEStructuralFeature(path.getAttribute());

		if (path.getNext() == null) {
			return new Pair(modelElement, feature);
		}
		assert feature.getEType() instanceof EClass;
		assert feature.getUpperBound() <= 1;
		return findModelElement((EObject) modelElement.eGet(feature), path
				.getNext());
	}

	private IObservableValue createEMFObservable(Pair pair) {
		return EMFObservables.observeValue(pair.getModelElement(), pair
				.getFeature());
	}
}